Skip to content
This repository was archived by the owner on Sep 8, 2026. It is now read-only.

solana-sdk: mirror ValidatorClientRewards as a zero-copy struct - #410

Merged
karl-dz merged 2 commits into
mainfrom
worktree-mirror-validator-client-rewards
Aug 14, 2026
Merged

solana-sdk: mirror ValidatorClientRewards as a zero-copy struct#410
karl-dz merged 2 commits into
mainfrom
worktree-mirror-validator-client-rewards

Conversation

@karl-dz

@karl-dz karl-dz commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Closes malbeclabs/doublezero#4183

Summary

solana-sdk

  • Add ValidatorClientRewards as a Pod mirror of the onchain struct, with a PrecomputedDiscriminator impl, a Default impl matching the program's, a checked_short_description accessor, and a compile-time assertion pinning the account at 184 bytes
  • Delete the five VCR_*_OFFSET constants, VCR_SHORT_DESCRIPTION_LEN, VCR_ACCOUNT_DATA_LEN, the standalone discriminator constant, parse_validator_client_rewards, and ValidatorClientRewardsInfo. None of them shipped, so the changelog bullet that introduced them is rewritten rather than paired with a removal entry
  • Fold the new mirror into the existing mirrored-layout section alongside ShredRewardToken and ValidatorPublisherRewards rather than opening a second one

solana-cli

  • Read the account through the mirror in show, claim, and init-holding. All three decode the whole struct, so they require at least the 184 bytes the program allocates
  • claim takes its post-transaction count from SolanaConnection::try_fetch_zero_copy_data_with_commitment, which warns on a missing or undecodable account where the previous code printed (unavailable) silently

solana-fork

  • Build the synthetic account from ValidatorClientRewards::default() plus field assignment instead of copying bytes to hand-written offsets, and size the rent exemption from the built buffer. The parse-back round trip and its three ensure! checks go with them, being tautological against a struct the same function builds
  • Rename --synthetic-vcr-manager to --synthetic-validator-client-rewards-manager, along with the fork test script and the local-validator workflow
  • That rename moves the environment variable clap derives from the flag, from SYNTHETIC_VCR_MANAGER to SYNTHETIC_VALIDATOR_CLIENT_REWARDS_MANAGER. A value left under the old name is ignored rather than rejected, so the fork boots with no synthetic account and the fork test fails later at the first validator-client-rewards show. The flag has only ever existed under ## [Unreleased], so no released version is affected, but anyone tracking main with the old name in a local .env or an infra job needs to move it

repo

  • Record the conventions this change was written under in CLAUDE.md: the _key suffix for pubkey bindings, and which helper to reach for when reading a zero-copy account

Testing

  • Unit tests across the three crates, including three cases for checked_short_description and an exact-match assertion pinning the rendered show summary byte for byte, which is what guards the format string's line continuations against a dropped escape
  • The local-validator workflow drives show, init-holding, and claim against a synthetic account baked into the fork at genesis, covering the renamed flag and the mirror's field offsets end to end

@karl-dz
karl-dz marked this pull request as ready for review August 14, 2026 16:42
@karl-dz
karl-dz requested a review from bgm-malbeclabs August 14, 2026 16:42
@karl-dz
karl-dz enabled auto-merge (squash) August 14, 2026 16:44

@bgm-malbeclabs bgm-malbeclabs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the change against the onchain source rather than only reading the diff.

What checks out: the new ValidatorClientRewards mirror in crates/solana-sdk/src/shred_subscription/state.rs is field for field identical to the program struct in doublezero-shred-subscription, including _padding_0, _padding_1, _gap: StorageGap<2>, and #[repr(C, align(8))]. Size works out to 176, so 184 - DISCRIMINATOR_LEN holds and the offsets match the deleted VCR_*_OFFSET constants exactly. discriminator_slice() returns the same 8 bytes the old borsh::to_vec produced, so the synthetic fork account stays byte identical. cargo clippy --all-targets -- -Dclippy::all is clean on the three affected packages, unit tests pass including the exact match show summary assertion, and the \ line continuations in the new format! reproduce the old two space indent. No references to the deleted API remain.

No correctness bug found. One note below, not a blocker.

Comment thread crates/solana-fork/src/main.rs
@karl-dz
karl-dz force-pushed the worktree-mirror-validator-client-rewards branch from 8915b37 to cc65a22 Compare August 14, 2026 17:21
@karl-dz
karl-dz merged commit 3010b57 into main Aug 14, 2026
6 checks passed
@karl-dz
karl-dz deleted the worktree-mirror-validator-client-rewards branch August 14, 2026 17:32
bgm-malbeclabs pushed a commit to malbeclabs/doublezero that referenced this pull request Aug 28, 2026
…eclabs/doublezero-offchain#410)

Closes #4183

## Summary

#### solana-sdk
- Add `ValidatorClientRewards` as a `Pod` mirror of the onchain struct,
with a `PrecomputedDiscriminator` impl, a `Default` impl matching the
program's, a `checked_short_description` accessor, and a compile-time
assertion pinning the account at 184 bytes
- Delete the five `VCR_*_OFFSET` constants, `VCR_SHORT_DESCRIPTION_LEN`,
`VCR_ACCOUNT_DATA_LEN`, the standalone discriminator constant,
`parse_validator_client_rewards`, and `ValidatorClientRewardsInfo`. None
of them shipped, so the changelog bullet that introduced them is
rewritten rather than paired with a removal entry
- Fold the new mirror into the existing mirrored-layout section
alongside `ShredRewardToken` and `ValidatorPublisherRewards` rather than
opening a second one

#### solana-cli
- Read the account through the mirror in `show`, `claim`, and
`init-holding`. All three decode the whole struct, so they require at
least the 184 bytes the program allocates
- `claim` takes its post-transaction count from
`SolanaConnection::try_fetch_zero_copy_data_with_commitment`, which
warns on a missing or undecodable account where the previous code
printed `(unavailable)` silently

#### solana-fork
- Build the synthetic account from `ValidatorClientRewards::default()`
plus field assignment instead of copying bytes to hand-written offsets,
and size the rent exemption from the built buffer. The parse-back round
trip and its three `ensure!` checks go with them, being tautological
against a struct the same function builds
- Rename `--synthetic-vcr-manager` to
`--synthetic-validator-client-rewards-manager`, along with the fork test
script and the local-validator workflow

#### repo
- Record the conventions this change was written under in `CLAUDE.md`:
the `_key` suffix for pubkey bindings, and which helper to reach for
when reading a zero-copy account

## Testing
- Unit tests across the three crates, including three cases for
`checked_short_description` and an exact-match assertion pinning the
rendered `show` summary byte for byte, which is what guards the format
string's line continuations against a dropped escape
- The `local-validator` workflow drives `show`, `init-holding`, and
`claim` against a synthetic account baked into the fork at genesis,
covering the renamed flag and the mirror's field offsets end to end
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

solana-sdk: mirror ValidatorClientRewards as a zero-copy struct

2 participants